﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.Globalization;
using System.Reflection;
using System.Text;
using System.Web.UI;
using System.ComponentModel;


namespace BizElements.BusinessLayer
{
    /// <summary>
    /// Provides common operations for array and list manipulation (conversions, sets...).
    /// </summary>
    public static class ArrayUtil
    {
        #region Conversions.

        /// <summary>
        /// Creates an array of objects with values in the given property.
        /// </summary>
        /// <param name="list">A list of objects that have the specified property.</param>
        /// <param name="property">Property that holds the values which are to be coppied to array.</param>
        /// <returns>Array of objects.</returns>
        public static object[] ToObjectArray(IList list, string property)
        {
            object[] array = new object[list.Count];
            for (int i = 0; i < list.Count; i++)
            {
                object val = DataBinder.Eval(list[i], property);
                array[i] = val;
            }

            return array;
        }

        /// <summary>
        /// Converts an ArrayList to an array of objects.
        /// </summary>
        /// <param name="list">Source list.</param>
        /// <returns>An array of objects which contains the same objects (same references) as the source list.</returns>
        public static object[] ToObjectArray(IList list)
        {
            object[] array = new object[list.Count];
            for (int i = 0; i < list.Count; i++)
                array[i] = list[i];

            return array;
        }

        /// <summary>
        /// Creates an array of integers with values in the given property.
        /// </summary>
        /// <param name="list">A list of objects that have the specified property.</param>
        /// <param name="property">Property that holds the numbers which are to be coppied to array.</param>
        /// <returns>Array of integers.</returns>
        public static int[] ToIntArray(IList list, string property)
        {
            int[] array = new int[list.Count];
            for (int i = 0; i < list.Count; i++)
            {
                int val = (int)DataBinder.Eval(list[i], property);
                array[i] = val;
            }

            return array;
        }

        /// <summary>
        /// Converts an ArrayList to an array of integers.
        /// </summary>
        /// <param name="list">Source list.</param>
        /// <returns>An array of integers which contains the same values (not references) as the source list.</returns>
        public static int[] ToIntArray(IList list)
        {
            int[] array = new int[list.Count];
            for (int i = 0; i < list.Count; i++)
                array[i] = (int)list[i];

            return array;
        }

        private static ArrayList ToArrayList(IEnumerable vector)
        {
            ArrayList list = new ArrayList();
            foreach (object o in vector)
                list.Add(o);

            return list;
        }

        /// <summary>
        /// Converts a list to array.
        /// </summary>
        /// <typeparam name="T">Type of objects contained in the list.</typeparam>
        /// <param name="list">A list of objects.</param>
        /// <returns>Array containg the same objects as the input list.</returns>
        public static T[] ToArray<T>(IList list)
        {
            T[] array = new T[list.Count];
            list.CopyTo(array, 0);
            return array;
        }

        /// <summary>
        /// Converts a collection to array.
        /// </summary>
        /// <typeparam name="T">Type of objects contained in the list.</typeparam>
        /// <param name="collection">A collection of objects.</param>
        /// <returns>Array containg the same objects as the input collection.</returns>
        public static T[] ToArray<T>(IEnumerable collection)
        {
            IList list = (collection is IList) ? (IList)collection : ArrayUtil.ToArrayList(collection);
            return ToArray<T>(list);
        }

        /// <summary>
        /// Creates an array that contains the values in the specified property.
        /// </summary>
        /// <typeparam name="T">Type of objects contained in the output array.</typeparam>
        /// <param name="list">A list of objects that have the specified property.</param>
        /// <param name="propertyName">Property that holds the numbers which are to be coppied to array.</param>
        /// <returns>Array.</returns>
        public static T[] ToArray<T>(IList list, string propertyName)
        {
            T[] array = new T[list.Count];
            PropertyInfo property = null;
            for (int i = 0; i < list.Count; i++)
            {
                object listElement = list[i];
                if (property == null)
                    property = listElement.GetType().GetProperty(propertyName);

                array[i] = (T)property.GetValue(listElement, null);
            }

            return array;
        }

        /// <summary>
        /// Creates an array that contains the values in the specified property.
        /// </summary>
        /// <typeparam name="T">Type of objects contained in the output array.</typeparam>
        /// <param name="collection">A collection of objects that have the specified property.</param>
        /// <param name="propertyName">Property that holds the numbers which are to be coppied to array.</param>
        /// <returns>Array.</returns>
        public static T[] ToArray<T>(IEnumerable collection, string propertyName)
        {
            IList list = (collection is IList) ? (IList)collection : ArrayUtil.ToArrayList(collection);
            return ToArray<T>(list, propertyName);
        }

        /// <summary>
        /// Creates an array that contains the values returned by the specified getter.
        /// </summary>
        /// <typeparam name="TProperty">Type of objects contained in the output array.</typeparam>
        /// <typeparam name="TElement">Type of elements contained in the collection.</typeparam>
        /// <param name="list">A list of objects that have the specified property.</param>
        /// <param name="getValue">A delegate to a method which returns values for output array.</param>
        /// <returns>Array.</returns>
        public static TProperty[] ToArray<TProperty, TElement>(ICollection list, Delegates.Function<TProperty, TElement> getValue)
        {
            TProperty[] array = new TProperty[list.Count];
            int idxElement = 0;
            foreach (TElement el in list)
            {
                array[idxElement] = getValue(el);
                idxElement++;
            }

            return array;
        }

        /// <summary>
        /// Creates an array that contains the values returned by the specified getter.
        /// </summary>
        /// <typeparam name="TProperty">Type of objects contained in the output array.</typeparam>
        /// <typeparam name="TElement">Type of elements contained in the collection.</typeparam>
        /// <param name="collection">A collection of objects that have the specified property.</param>
        /// <param name="getValue">A delegate to a method which returns values for output array.</param>
        /// <returns>Array.</returns>
        public static TProperty[] ToArray<TProperty, TElement>(IEnumerable collection, Delegates.Function<TProperty, TElement> getValue)
        {
            int size = 0;
            foreach (object el in collection)
                size++;

            TProperty[] array = new TProperty[size];
            int idxElement = 0;
            foreach (TElement el in collection)
            {
                array[idxElement] = getValue(el);
                idxElement++;
            }

            return array;
        }

        /// <summary>
        /// Converts all objects from the source collection and adds them to the new list.
        /// </summary>
        /// <typeparam name="TList">Type of the list which is returned from the method.</typeparam>
        /// <typeparam name="TInputElement">Type of elements contained in the source list.</typeparam>
        /// <typeparam name="TOutElement">Type of elements contained in the output list.</typeparam>
        /// <param name="collection">Source list.</param>
        /// <param name="conversion">A delegate which converts an input to output element.</param>
        /// <returns>A list which contains converted elements.</returns>
        public static TList Convert<TList, TInputElement, TOutElement>(IEnumerable collection, Delegates.Function<TOutElement, TInputElement> conversion)
            where TList : IList, new()
        {
            TList outList = new TList();
            foreach (TInputElement inElement in collection)
            {
                TOutElement outElement = conversion(inElement);
                outList.Add(outElement);
            }

            return outList;
        }

        /// <summary>
        /// Converts all objects in the list.
        /// </summary>
        /// <typeparam name="TInputElement">Type of elements contained in the source list.</typeparam>
        /// <typeparam name="TOutElement">Type of elements contained in the output list.</typeparam>
        /// <param name="list">List whose elements are to be converted.</param>
        /// <param name="conversion">A delegate which converts an input to output element.</param>
        public static void ConvertElements<TInputElement, TOutElement>(IList list, Delegates.Function<TOutElement, TInputElement> conversion)
        {
            for (int idx = 0; idx < list.Count; idx++)
            {
                TInputElement inElement = (TInputElement)list[idx];
                TOutElement outElement = conversion(inElement);
                list[idx] = outElement;
            }
        }

        /// <summary>
        /// Creates a dictionary from a given collection.
        /// </summary>
        /// <typeparam name="TDictionary">Type of dictionary returned from the method.</typeparam>
        /// <typeparam name="TKey">The type of keys in the dictionary.</typeparam>
        /// <typeparam name="TValue">The type of values in the dictionary.</typeparam>
        /// <typeparam name="TInputElement">Type of elements contained in the source list.</typeparam>
        /// <param name="collection">Source list.</param>
        /// <param name="conversion">A delegate which converts an input element to a key-value pair.</param>
        /// <returns>A dictionary which contains converted key-value pairs.</returns>
        public static TDictionary ToDictionary<TDictionary, TKey, TValue, TInputElement>(IEnumerable collection, Delegates.Function<KeyValuePair<TKey, TValue>, TInputElement> conversion)
            where TDictionary : IDictionary<TKey, TValue>, new()
        {
            TDictionary dict = new TDictionary();
            foreach (TInputElement element in collection)
            {
                var keyAndValue = conversion(element);
                dict[keyAndValue.Key] = keyAndValue.Value;
            }

            return dict;
        }

        /// <summary>
        /// Creates a dictionary from a given collection.
        /// </summary>
        /// <typeparam name="TDictionary">Type of dictionary returned from the method.</typeparam>
        /// <typeparam name="TKey">The type of keys in the dictionary.</typeparam>
        /// <typeparam name="TValue">The type of values in the dictionary.</typeparam>
        /// <typeparam name="TInputElement">Type of elements contained in the source list.</typeparam>
        /// <param name="listSource">Source list.</param>
        /// <param name="conversion">A delegate which converts an input element to a key-value pair.</param>
        /// <returns>A dictionary which contains converted key-value pairs.</returns>
        public static TDictionary ToDictionary<TDictionary, TKey, TValue, TInputElement>(IListSource listSource, Delegates.Function<KeyValuePair<TKey, TValue>, TInputElement> conversion)
            where TDictionary : IDictionary<TKey, TValue>, new()
        {
            IList collection = listSource.GetList();
            TDictionary dict = new TDictionary();
            foreach (TInputElement element in collection)
            {
                var keyAndValue = conversion(element);
                dict[keyAndValue.Key] = keyAndValue.Value;
            }

            return dict;
        }

        /// <summary>
        /// Creates a dictionary from a given <see cref="System.Data.DataTable"/>.
        /// </summary>
        /// <typeparam name="TDictionary">Type of dictionary returned from the method.</typeparam>
        /// <typeparam name="TKey">The type of keys in the dictionary.</typeparam>
        /// <typeparam name="TValue">The type of values in the dictionary.</typeparam>
        /// <param name="data">Source data.</param>
        /// <param name="conversion">A delegate which converts a <see cref="System.Data.DataRow"/> to a key-value pair.</param>
        /// <returns>A dictionary which contains converted key-value pairs.</returns>
        public static TDictionary ToDictionary<TDictionary, TKey, TValue>(DataTable data, Delegates.Function<KeyValuePair<TKey, TValue>, DataRow> conversion)
            where TDictionary : IDictionary<TKey, TValue>, new()
        {
            TDictionary dict = new TDictionary();
            foreach (DataRow row in data.Rows)
            {
                var keyAndValue = conversion(row);
                dict[keyAndValue.Key] = keyAndValue.Value;
            }

            return dict;
        }

        /// <summary>
        /// Creates a dictionary from a given <see cref="System.Data.DataTable"/>.
        /// </summary>
        /// <typeparam name="TDictionary">Type of dictionary returned from the method.</typeparam>
        /// <typeparam name="TKey">The type of keys in the dictionary.</typeparam>
        /// <typeparam name="TValue">The type of values in the dictionary.</typeparam>
        /// <param name="data">Source data.</param>
        /// <param name="keyColumnIndex">Index of <see cref="System.Data.DataColumn"/> which contains dictionary keys.</param>
        /// <param name="valueColumnIndex">Index of <see cref="System.Data.DataColumn"/> which contains dictionary values.</param>
        /// <returns>A dictionary which contains key-value pairs created from data in specified columns.</returns>
        public static TDictionary ToDictionary<TDictionary, TKey, TValue>(DataTable data, int keyColumnIndex, int valueColumnIndex)
            where TDictionary : IDictionary<TKey, TValue>, new()
        {
            return ToDictionary<TDictionary, TKey, TValue>(data,
                (row) => new KeyValuePair<TKey, TValue>(TryCast<TKey>(row[keyColumnIndex]), TryCast<TValue>(row[valueColumnIndex])));
        }

        /// <summary>
        /// Creates a dictionary from a given <see cref="System.Data.DataTable"/>.
        /// </summary>
        /// <typeparam name="TDictionary">Type of dictionary returned from the method.</typeparam>
        /// <typeparam name="TKey">The type of keys in the dictionary.</typeparam>
        /// <typeparam name="TValue">The type of values in the dictionary.</typeparam>
        /// <param name="data">Source data.</param>
        /// <param name="keyColumnName">Name of <see cref="System.Data.DataColumn"/> which contains dictionary keys.</param>
        /// <param name="valueColumnName">Name of <see cref="System.Data.DataColumn"/> which contains dictionary values.</param>
        /// <returns>A dictionary which contains key-value pairs created from data in specified columns.</returns>
        public static TDictionary ToDictionary<TDictionary, TKey, TValue>(DataTable data, string keyColumnName, string valueColumnName)
            where TDictionary : IDictionary<TKey, TValue>, new()
        {
            int keyColumnIndex = data.Columns.IndexOf(keyColumnName);
            int valueColumnIndex = data.Columns.IndexOf(valueColumnName);
            return ToDictionary<TDictionary, TKey, TValue>(data, keyColumnIndex, valueColumnIndex);
        }

        private static T TryCast<T>(object value)
        {
            return (value is T) ? (T)value : default(T);
        }

        #endregion

        #region Object filtering, indexes.

        /// <summary>
        /// Gets all objects with the specified keys.
        /// </summary>
        /// <param name="allObjects">Source list.</param>
        /// <param name="keyProperty">Name of the property that contains key values.</param>
        /// <param name="allowedKeyValues">Key values of object that will be added to output list.</param>
        /// <returns>A list of objects with the specified keys.</returns>
        public static ArrayList GetObjectsWithKeys(IList allObjects, string keyProperty, params int[] allowedKeyValues)
        {
            ArrayList validObjects = new ArrayList();
            int[] allKeys = ToIntArray(allObjects, keyProperty);
            for (int i = 0; i < allKeys.Length; i++)
            {
                if (Contains(allowedKeyValues, allKeys[i]))
                    validObjects.Add(allObjects[i]);
            }

            return validObjects;
        }

        /// <summary>
        /// Gets all object with the specified keys.
        /// </summary>
        /// <typeparam name="TList">Type of the list which is returned from the method.</typeparam>
        /// <typeparam name="TElement">Type of elements contained in the input and output lists.</typeparam>
        /// <typeparam name="TKey">Type of the values in the key property.</typeparam>
        /// <param name="allObjects">Source list.</param>
        /// <param name="keyPropertyName">Name of the property that contains key values.</param>
        /// <param name="allowedKeyValues">Key values of object that will be added to output list.</param>
        /// <returns>A list of objects with the specified keys.</returns>
        public static TList GetObjectsWithKeys<TList, TElement, TKey>(IEnumerable allObjects, string keyPropertyName, params TKey[] allowedKeyValues)
            where TList : IList, new()
        {
            TList validObjects = new TList();
            PropertyInfo keyProperty = typeof(TElement).GetProperty(keyPropertyName);
            foreach (TElement obj in allObjects)
            {
                TKey key = (TKey)keyProperty.GetValue(obj, null);
                bool isAllowed = (Array.IndexOf<TKey>(allowedKeyValues, key) >= 0);
                if (isAllowed)
                    validObjects.Add(obj);
            }

            return validObjects;
        }

        /// <summary>
        /// Gets a single object with the specified key.
        /// </summary>
        /// <param name="allObjects">Source list.</param>
        /// <param name="keyProperty">Name of the property that contains key values.</param>
        /// <param name="key">Key value of object that is to be found in the source list.</param>
        /// <returns>Object with the given key; null if not found.</returns>
        public static object GetObjectByKey(IList allObjects, string keyProperty, object key)
        {
            int objectIndex = IndexOf(allObjects, keyProperty, key);
            if (objectIndex >= 0)
                return allObjects[objectIndex];
            else
                return null;
        }

        /// <summary>
        /// Gets a singe object with the specified key.
        /// </summary>
        /// <typeparam name="TElement">Type of elements contained in the input and output lists.</typeparam>
        /// <typeparam name="TKey">Type of the values in the key property.</typeparam>
        /// <param name="allObjects">Source list.</param>
        /// <param name="keyPropertyName">Name of the property that contains key values.</param>
        /// <param name="key">Key value of object that is to be found in the source list.</param>
        /// <returns>Object with the given key; null if not found.</returns>
        public static TElement GetObjectByKey<TElement, TKey>(IEnumerable allObjects, string keyPropertyName, TKey key)
            where TElement : class
            where TKey : IEquatable<TKey>
        {
            PropertyInfo keyProperty = typeof(TElement).GetProperty(keyPropertyName);
            foreach (TElement obj in allObjects)
            {
                TKey currObjKey = (TKey)keyProperty.GetValue(obj, null);
                bool isMatch = currObjKey.Equals(key);
                if (isMatch)
                    return obj;
            }

            return null;
        }

        /// <summary>
        /// Gets all object that don't have the specified keys.
        /// </summary>
        /// <param name="allObjects">Source list.</param>
        /// <param name="keyProperty">Name of the property that contains key values.</param>
        /// <param name="excludedKeyValues">Key values of object that will <b>not</b> be added to output list.</param>
        /// <returns>A list of objects without the specified keys.</returns>
        public static ArrayList GetObjectsWithoutKeys(IList allObjects, string keyProperty, params int[] excludedKeyValues)
        {
            ArrayList validObjects = new ArrayList();
            foreach (object obj in allObjects)
            {
                int currKey = (int)DataBinder.Eval(obj, keyProperty);
                bool isExcludedKey = ArrayUtil.Contains(excludedKeyValues, currKey);
                if (!isExcludedKey)
                    validObjects.Add(obj);
            }

            return validObjects;
        }

        /// <summary>
        /// Gets all object that don't have the specified keys.
        /// </summary>
        /// <typeparam name="TList">Type of the list which is returned from the method.</typeparam>
        /// <typeparam name="TElement">Type of elements contained in the input and output lists.</typeparam>
        /// <typeparam name="TKey">Type of the values in the key property.</typeparam>
        /// <param name="allObjects">Source list.</param>
        /// <param name="keyPropertyName">Name of the property that contains key values.</param>
        /// <param name="excludedKeyValues">Key values of object that will <b>not</b> be added to output list.</param>
        /// <returns>A list of objects without the specified keys.</returns>
        public static TList GetObjectWithoutKeys<TList, TElement, TKey>(IEnumerable allObjects, string keyPropertyName, params TKey[] excludedKeyValues)
            where TList : IList, new()
        {
            TList validObjects = new TList();
            PropertyInfo keyProperty = typeof(TElement).GetProperty(keyPropertyName);
            foreach (TElement obj in allObjects)
            {
                TKey key = (TKey)keyProperty.GetValue(obj, null);
                bool isAllowed = (Array.IndexOf<TKey>(excludedKeyValues, key) < 0);
                if (isAllowed)
                    validObjects.Add(obj);
            }

            return validObjects;
        }

        /// <summary>
        /// Returns zero-based index of an object with the specified key.
        /// </summary>
        /// <param name="objects">List of objects.</param>
        /// <param name="keyProperty">Name of the property that contains key values.</param>
        /// <param name="key">Key value of object that is to be found in the source list.</param>
        /// <returns>Index of the object; -1 if not found.</returns>
        public static int IndexOf(IList objects, string keyProperty, object key)
        {
            for (int index = 0; index < objects.Count; index++)
            {
                object currKey = DataBinder.Eval(objects[index], keyProperty);
                if (currKey.Equals(key))
                    return index;
            }

            return -1;
        }

        /// <summary>
        /// Returns zero-based index of an object with the specified key.
        /// </summary>
        /// <typeparam name="TKey">Type of the values in the key property.</typeparam>
        /// <param name="objects">A list or an array of objects.</param>
        /// <param name="keyPropertyName">Name of the property that contains key values.</param>
        /// <param name="key">Key value of object that is to be found in the source list.</param>
        /// <returns>Index of the object; -1 if not found.</returns>
        public static int IndexOf<TKey>(IEnumerable objects, string keyPropertyName, TKey key)
            where TKey : IEquatable<TKey>
        {
            PropertyInfo keyProperty = null;
            int index = 0;
            foreach (object obj in objects)
            {
                if (keyProperty == null)
                    keyProperty = obj.GetType().GetProperty(keyPropertyName);

                TKey currObjKey = (TKey)keyProperty.GetValue(obj, null);
                bool isMatch = currObjKey.Equals(key);
                if (isMatch)
                    return index;

                index++;
            }

            return -1;
        }

        /// <summary>
        /// Returns zero-based index of a first occurance of object which matches the specified criteria.
        /// </summary>
        /// <typeparam name="TElement">Type of elements contained in the collection.</typeparam>
        /// <param name="collection">A collection.</param>
        /// <param name="match">The Predicate(TElement) delegate that defines the conditions of the elements to search for.</param>
        /// <returns>Index of the element; -1 if not found.</returns>
        public static int IndexOf<TElement>(IEnumerable collection, Predicate<TElement> match)
        {
            int currIndex = 0;
            foreach (TElement element in collection)
            {
                if (match(element))
                    return currIndex;

                currIndex++;
            }

            return -1;
        }

        /// <summary>
        /// Searches for the specified object and returns the zero-based index of the first occurrence within the entire collection.
        /// </summary>
        /// <param name="collection">A collection.</param>
        /// <param name="item">The object to locate in the collection. The value can be <b>null</b> for reference types.</param>
        /// <returns>The zero-based index of the first occurrence of item within the entire collection, if found; otherwise, –1.</returns>
        public static int IndexOf(IEnumerable collection, object item)
        {
            int currIndex = 0;
            foreach (object currItem in collection)
            {
                if (item == currItem)
                    return currIndex;

                currIndex++;
            }

            return -1;
        }

        /// <summary>
        /// Removes list items at the specified indexes.
        /// </summary>
        /// <param name="list">A list form which items are to be removed.</param>
        /// <param name="indexes">The zero-based indexes of items to remove.</param>
        public static void RemoveAt(IList list, params int[] indexes)
        {
            if (ArrayUtil.IsNullOrEmpty(indexes))
                return;

            int[] itemsToRemove = new int[indexes.Length];
            indexes.CopyTo(itemsToRemove, 0);
            Array.Sort<int>(itemsToRemove);
            for (int idxReversed = itemsToRemove.Length - 1; idxReversed >= 0; idxReversed--)
                list.RemoveAt(itemsToRemove[idxReversed]);
        }

        #endregion

        #region Predicates.

        /// <summary>
        /// Determines whether the collection contains elements that match the conditions defined by the specified predicate.
        /// </summary>
        /// <typeparam name="TElement">Type of elements contained in the collection.</typeparam>
        /// <param name="collection">A collection.</param>
        /// <param name="match">The Predicate(TElement) delegate that defines the conditions of the elements to search for.</param>
        /// <returns>true if the collection contains one or more elements that match the conditions defined by the specified predicate; otherwise, false.</returns>
        public static bool Exists<TElement>(IEnumerable collection, Predicate<TElement> match)
        {
            foreach (TElement obj in collection)
            {
                if (match(obj))
                    return true;
            }

            return false;
        }

        /// <summary>
        /// Searches for an element that matches the conditions defined by the specified predicate, and returns the first occurrence within the entire collection.
        /// </summary>
        /// <typeparam name="TElement">Type of elements contained in the collection.</typeparam>
        /// <param name="collection">A collection.</param>
        /// <param name="match">The Predicate(TElement) delegate that defines the conditions of the elements to search for.</param>
        /// <returns>The first element that matches the conditions defined by the specified predicate, if found; otherwise, the default value for type TElement.</returns>
        public static TElement Find<TElement>(IEnumerable collection, Predicate<TElement> match)
        {
            foreach (TElement obj in collection)
            {
                if (match(obj))
                    return obj;
            }

            return default(TElement);
        }

        /// <summary>
        /// Retrieves all the elements that match the conditions defined by the specified predicate.
        /// </summary>
        /// <typeparam name="TList">Type of the list which is returned from the method.</typeparam>
        /// <typeparam name="TElement">Type of elements contained in the collection.</typeparam>
        /// <param name="collection">A collection.</param>
        /// <param name="match">The Predicate(TElement) delegate that defines the conditions of the elements to search for.</param>
        /// <returns>A list containing all the elements that match the conditions defined by the specified predicate, if found; otherwise, an empty list.</returns>
        public static TList FindAll<TList, TElement>(IEnumerable collection, Predicate<TElement> match)
            where TList : IList, new()
        {
            TList matches = new TList();
            foreach (TElement obj in collection)
            {
                if (match(obj))
                    matches.Add(obj);
            }

            return matches;
        }

        /// <summary>
        /// Searches for an element that matches the conditions defined by the specified predicate, and returns the zero-based index of the first occurrence within the entire collection.
        /// </summary>
        /// <typeparam name="TElement">Type of elements contained in the collection.</typeparam>
        /// <param name="collection">A collection.</param>
        /// <param name="match">The Predicate(TElement) delegate that defines the conditions of the elements to search for.</param>
        /// <returns>The zero-based index of the first occurrence of an element that matches the conditions defined by match, if found; otherwise, –1.</returns>
        public static int FindIndex<TElement>(IEnumerable collection, Predicate<TElement> match)
        {
            int index = 0;
            foreach (TElement obj in collection)
            {
                if (match(obj))
                    return index;

                index++;
            }

            return -1;
        }

        /// <summary>
        /// Searches for all elements that match the conditions defined by the specified predicate, and returns the zero-based indexes of all occurrences within the collection.
        /// </summary>
        /// <typeparam name="TElement">Type of elements contained in the collection.</typeparam>
        /// <param name="collection">A collection.</param>
        /// <param name="match">The Predicate(TElement) delegate that defines the conditions of the elements to search for.</param>
        /// <returns>Array of zero-based indexes of elements that match the conditions defined by match. Empty array if none of the elements in the collection match the criteria.</returns>
        public static int[] FindAllIndexes<TElement>(IEnumerable collection, Predicate<TElement> match)
        {
            List<int> indexes = new List<int>();
            int i = 0;
            foreach (TElement obj in collection)
            {
                if (match(obj))
                    indexes.Add(i);

                i++;
            }

            return indexes.ToArray();
        }

        /// <summary>
        /// Searches for an element that matches the conditions defined by the specified predicate, and returns the zero-based index of the first occurrence within the range of elements in the collection that extends from the specified index to the last element.
        /// </summary>
        /// <typeparam name="TElement">Type of elements contained in the collection.</typeparam>
        /// <param name="collection">A collection.</param>
        /// <param name="startIndex">The zero-based starting index of the search.</param>
        /// <param name="match">The Predicate(TElement) delegate that defines the conditions of the elements to search for.</param>
        /// <returns>The zero-based index of the first occurrence of an element that matches the conditions defined by match, if found; otherwise, –1.</returns>
        public static int FindIndex<TElement>(IEnumerable collection, int startIndex, Predicate<TElement> match)
        {
            int index = 0;
            foreach (TElement obj in collection)
            {
                bool isMatch = (index >= startIndex) && match(obj);
                if (isMatch)
                    return index;

                index++;
            }

            return -1;
        }

        /// <summary>
        /// Searches for an element that matches the conditions defined by the specified predicate, and returns the zero-based index of the first occurrence within the range of elements in the array that extends from the specified index to the last element.
        /// </summary>
        /// <typeparam name="TElement">Type of elements contained in the array.</typeparam>
        /// <param name="array">An array.</param>
        /// <param name="startIndex">The zero-based starting index of the search.</param>
        /// <param name="match">The Predicate(TElement) delegate that defines the conditions of the elements to search for.</param>
        /// <returns>The zero-based index of the first occurrence of an element that matches the conditions defined by match, if found; otherwise, –1.</returns>
        public static int FindIndex<TElement>(Array array, int startIndex, Predicate<TElement> match)
        {
            for (int index = startIndex; index < array.Length; index++)
            {
                TElement obj = (TElement)array.GetValue(index);
                if (match(obj))
                    return index;
            }

            return -1;
        }

        /// <summary>
        /// Searches for an element that matches the conditions defined by the specified predicate, and returns the zero-based index of the first occurrence within the range of elements in the list that extends from the specified index to the last element.
        /// </summary>
        /// <typeparam name="TElement">Type of elements contained in the list.</typeparam>
        /// <param name="list">A list.</param>
        /// <param name="startIndex">The zero-based starting index of the search.</param>
        /// <param name="match">The Predicate(TElement) delegate that defines the conditions of the elements to search for.</param>
        /// <returns>The zero-based index of the first occurrence of an element that matches the conditions defined by match, if found; otherwise, –1.</returns>
        public static int FindIndex<TElement>(IList list, int startIndex, Predicate<TElement> match)
        {
            for (int index = startIndex; index < list.Count; index++)
            {
                TElement obj = (TElement)list[index];
                if (match(obj))
                    return index;
            }

            return -1;
        }

        /// <summary>
        /// Searches for an element that matches the conditions defined by the specified predicate, and returns the last occurrence within the entire collection.
        /// </summary>
        /// <typeparam name="TElement">Type of elements contained in the collection.</typeparam>
        /// <param name="collection">A collection.</param>
        /// <param name="match">The Predicate(TElement) delegate that defines the conditions of the elements to search for.</param>
        /// <returns>The last element that matches the conditions defined by the specified predicate, if found; otherwise, the default value for type TElement.</returns>
        public static TElement FindLast<TElement>(IEnumerable collection, Predicate<TElement> match)
        {
            TElement lastMatch = default(TElement);
            foreach (TElement obj in collection)
            {
                if (match(obj))
                    lastMatch = obj;
            }

            return lastMatch;
        }

        /// <summary>
        /// Searches for an element that matches the conditions defined by the specified predicate, and returns the last occurrence within the entire array.
        /// </summary>
        /// <typeparam name="TElement">Type of elements contained in the array.</typeparam>
        /// <param name="array">An array.</param>
        /// <param name="match">The Predicate(TElement) delegate that defines the conditions of the elements to search for.</param>
        /// <returns>The last element that matches the conditions defined by the specified predicate, if found; otherwise, the default value for type TElement.</returns>
        public static TElement FindLast<TElement>(Array array, Predicate<TElement> match)
        {
            for (int descIndex = array.Length - 1; descIndex >= 0; descIndex--)
            {
                TElement obj = (TElement)array.GetValue(descIndex);
                if (match(obj))
                    return obj;
            }

            return default(TElement);
        }

        /// <summary>
        /// Searches for an element that matches the conditions defined by the specified predicate, and returns the last occurrence within the entire list.
        /// </summary>
        /// <typeparam name="TElement">Type of elements contained in the list.</typeparam>
        /// <param name="list">A list.</param>
        /// <param name="match">The Predicate(TElement) delegate that defines the conditions of the elements to search for.</param>
        /// <returns>The last element that matches the conditions defined by the specified predicate, if found; otherwise, the default value for type TElement.</returns>
        public static TElement FindLast<TElement>(IList list, Predicate<TElement> match)
        {
            for (int descIndex = list.Count - 1; descIndex >= 0; descIndex--)
            {
                TElement obj = (TElement)list[descIndex];
                if (match(obj))
                    return obj;
            }

            return default(TElement);
        }

        /// <summary>
        /// Searches for an element that matches the conditions defined by the specified predicate, and returns the zero-based index of the last occurrence within the entire collection.
        /// </summary>
        /// <typeparam name="TElement">Type of elements contained in the collection.</typeparam>
        /// <param name="collection">A collection.</param>
        /// <param name="match">The Predicate(TElement) delegate that defines the conditions of the elements to search for.</param>
        /// <returns>The zero-based index of the last occurrence of an element that matches the conditions defined by match, if found; otherwise, –1.</returns>
        public static int FindLastIndex<TElement>(IEnumerable collection, Predicate<TElement> match)
        {
            int index = 0;
            int lastMatch = -1;
            foreach (TElement obj in collection)
            {
                if (match(obj))
                    lastMatch = index;

                index++;
            }

            return lastMatch;
        }


        /// <summary>
        /// Searches for an element that matches the conditions defined by the specified predicate, and returns the zero-based index of the last occurrence within the entire array.
        /// </summary>
        /// <typeparam name="TElement">Type of elements contained in the array.</typeparam>
        /// <param name="array">An array.</param>
        /// <param name="match">The Predicate(TElement) delegate that defines the conditions of the elements to search for.</param>
        /// <returns>The zero-based index of the last occurrence of an element that matches the conditions defined by match, if found; otherwise, –1.</returns>
        public static int FindLastIndex<TElement>(Array array, Predicate<TElement> match)
        {
            for (int descIndex = array.Length - 1; descIndex >= 0; descIndex--)
            {
                TElement obj = (TElement)array.GetValue(descIndex);
                if (match(obj))
                    return descIndex;
            }

            return -1;
        }

        /// <summary>
        /// Searches for an element that matches the conditions defined by the specified predicate, and returns the zero-based index of the last occurrence within the entire list.
        /// </summary>
        /// <typeparam name="TElement">Type of elements contained in the list.</typeparam>
        /// <param name="list">A list.</param>
        /// <param name="match">The Predicate(TElement) delegate that defines the conditions of the elements to search for.</param>
        /// <returns>The zero-based index of the last occurrence of an element that matches the conditions defined by match, if found; otherwise, –1.</returns>
        public static int FindLastIndex<TElement>(IList list, Predicate<TElement> match)
        {
            for (int descIndex = list.Count - 1; descIndex >= 0; descIndex--)
            {
                TElement obj = (TElement)list[descIndex];
                if (match(obj))
                    return descIndex;
            }

            return -1;
        }

        /// <summary>
        /// Removes the all the elements that match the conditions defined by the specified predicate.
        /// </summary>
        /// <typeparam name="TElement">Type of elements contained in the list.</typeparam>
        /// <param name="list">A list.</param>
        /// <param name="match">The Predicate(TElement) delegate that defines the conditions of the elements to search for.</param>
        /// <returns>The number of elements removed from the list.</returns>
        public static int RemoveAll<TElement>(IList list, Predicate<TElement> match)
        {
            List<int> indexesToRemove = new List<int>();
            for (int idxOfTested = 0; idxOfTested < list.Count; idxOfTested++)
            {
                TElement obj = (TElement)list[idxOfTested];
                if (match(obj))
                    indexesToRemove.Add(idxOfTested);
            }

            if (indexesToRemove.Count > 0)
                RemoveAt(list, indexesToRemove.ToArray());

            return indexesToRemove.Count;
        }

        /// <summary>
        /// Determines whether every element in the collection matches the conditions defined by the specified predicate.
        /// </summary>
        /// <typeparam name="TElement">Type of elements contained in the list.</typeparam>
        /// <param name="collection">A collection.</param>
        /// <param name="match">The Predicate(TElement) delegate that defines the conditions to check against the elements.</param>
        /// <returns>true if every element in the collection matches the conditions defined by the specified predicate; otherwise, false. If the list has no elements, the return value is true.</returns>
        public static bool TrueForAll<TElement>(IEnumerable collection, Predicate<TElement> match)
        {
            foreach (TElement obj in collection)
            {
                if (!match(obj))
                    return false;
            }

            return true;
        }

        #endregion

        #region IsNullOrEmpty.

        /// <summary>
        /// Checks whether the specified array is a null reference or an empty array.
        /// </summary>
        /// <param name="array">Array to check.</param>
        /// <returns><b>true</b> if the provided array is <b>null</b> or empty; otherwise, false.</returns>
        public static bool IsNullOrEmpty(Array array)
        {
            bool isNullOrEmtpy = (array == null) || (array.Length == 0);
            return isNullOrEmtpy;
        }

        /// <summary>
        /// Checks whether the specified collection is a null reference or an empty collection.
        /// </summary>
        /// <param name="collection">Collection to check.</param>
        /// <returns><b>true</b> if the provided collection is <b>null</b> or empty; otherwise, false.</returns>
        public static bool IsNullOrEmpty(ICollection collection)
        {
            bool isNullOrEmtpy = (collection == null) || (collection.Count == 0);
            return isNullOrEmtpy;
        }

        /// <summary>
        /// Checks whether the specified collection is a null reference or an empty collection.
        /// </summary>
        /// <param name="collection">Collection to check.</param>
        /// <returns><b>true</b> if the provided collection is <b>null</b> or empty; otherwise, false.</returns>
        public static bool IsNullOrEmpty(IEnumerable collection)
        {
            if (collection == null)
                return true;

            IEnumerator enumerator = collection.GetEnumerator();
            bool isEmpty = !enumerator.MoveNext();
            return isEmpty;
        }

        #endregion

        #region Get/set first/last element.

        /// <summary>
        /// Returns last element in the list.
        /// </summary>
        /// <typeparam name="TElement">Type of elements contained in the input list.</typeparam>
        /// <param name="list">A list of objects.</param>
        /// <returns>Last element or null if the list is empty or null.</returns>
        public static TElement? GetLastValue<TElement>(IList list)
            where TElement : struct
        {
            TElement? lastElement;
            if (ArrayUtil.IsNullOrEmpty(list))
                lastElement = null;
            else
                lastElement = (TElement)list[list.Count - 1];

            return lastElement;
        }

        /// <summary>
        /// Returns last element in the array.
        /// </summary>
        /// <typeparam name="TElement">Type of elements contained in the input array.</typeparam>
        /// <param name="array">An array of objects.</param>
        /// <returns>Last element or null if the array is empty or null.</returns>
        public static TElement? GetLastValue<TElement>(Array array)
            where TElement : struct
        {
            TElement? lastElement;
            if (ArrayUtil.IsNullOrEmpty(array))
                lastElement = null;
            else
                lastElement = (TElement)array.GetValue(array.Length - 1);

            return lastElement;
        }

        /// <summary>
        /// Returns last element in the collection.
        /// </summary>
        /// <typeparam name="TElement">Type of elements contained in the input collection.</typeparam>
        /// <param name="collection">A collection of objects.</param>
        /// <returns>Last element or null if the collection is empty or null.</returns>
        public static TElement? GetLastValue<TElement>(IEnumerable collection)
            where TElement : struct
        {
            TElement? lastElement = null;
            if (collection != null)
            {
                foreach (TElement element in collection)
                    lastElement = element;
            }

            return lastElement;
        }

        /// <summary>
        /// Returns last element in the list.
        /// </summary>
        /// <typeparam name="TElement">Type of elements contained in the input list.</typeparam>
        /// <param name="list">A list of objects.</param>
        /// <returns>Last element or null if the list is empty or null.</returns>
        public static TElement GetLastObject<TElement>(IList list)
            where TElement : class
        {
            TElement lastElement;
            if (ArrayUtil.IsNullOrEmpty(list))
                lastElement = null;
            else
                lastElement = (TElement)list[list.Count - 1];

            return lastElement;
        }

        /// <summary>
        /// Returns last element in the array.
        /// </summary>
        /// <typeparam name="TElement">Type of elements contained in the input array.</typeparam>
        /// <param name="array">An array of objects.</param>
        /// <returns>Last element or null if the array is empty or null.</returns>
        public static TElement GetLastObject<TElement>(Array array)
            where TElement : class
        {
            TElement lastElement;
            if (ArrayUtil.IsNullOrEmpty(array))
                lastElement = null;
            else
                lastElement = (TElement)array.GetValue(array.Length - 1);

            return lastElement;
        }

        /// <summary>
        /// Returns last element in the collection.
        /// </summary>
        /// <typeparam name="TElement">Type of elements contained in the input collection.</typeparam>
        /// <param name="collection">A collection of objects.</param>
        /// <returns>Last element or null if the collection is empty or null.</returns>
        public static TElement GetLastObject<TElement>(IEnumerable collection)
            where TElement : class
        {
            TElement lastElement = null;
            if (collection != null)
            {
                foreach (TElement element in collection)
                    lastElement = element;
            }

            return lastElement;
        }

        /// <summary>
        /// Returns first element in the list.
        /// </summary>
        /// <typeparam name="TElement">Type of elements contained in the input list.</typeparam>
        /// <param name="list">A list of objects.</param>
        /// <returns>First element or null if the list is empty or null.</returns>
        public static TElement? GetFirstValue<TElement>(IList list)
            where TElement : struct
        {
            TElement? firstElement;
            if (ArrayUtil.IsNullOrEmpty(list))
                firstElement = null;
            else
                firstElement = (TElement)list[0];

            return firstElement;
        }

        /// <summary>
        /// Returns first element in the list.
        /// </summary>
        /// <typeparam name="TElement">Type of elements contained in the input list.</typeparam>
        /// <param name="array">An array of objects.</param>
        /// <returns>First element or null if the list is empty or null.</returns>
        public static TElement? GetFirstValue<TElement>(Array array)
            where TElement : struct
        {
            TElement? firstElement;
            if (ArrayUtil.IsNullOrEmpty(array))
                firstElement = null;
            else
                firstElement = (TElement)array.GetValue(0);

            return firstElement;
        }

        /// <summary>
        /// Returns first element in the list.
        /// </summary>
        /// <typeparam name="TElement">Type of elements contained in the input list.</typeparam>
        /// <param name="collection">A collection of objects.</param>
        /// <returns>First element or null if the list is empty or null.</returns>
        public static TElement? GetFirstValue<TElement>(IEnumerable collection)
            where TElement : struct
        {
            TElement? firstElement = null;
            if (collection != null)
            {
                foreach (TElement element in collection)
                {
                    firstElement = element;
                    break;
                }
            }

            return firstElement;
        }

        /// <summary>
        /// Returns first element in the list.
        /// </summary>
        /// <typeparam name="TElement">Type of elements contained in the input list.</typeparam>
        /// <param name="list">A list of objects.</param>
        /// <returns>First element or null if the list is empty or null.</returns>
        public static TElement GetFirstObject<TElement>(IList list)
            where TElement : class
        {
            TElement firstElement;
            if (ArrayUtil.IsNullOrEmpty(list))
                firstElement = null;
            else
                firstElement = (TElement)list[0];

            return firstElement;
        }

        /// <summary>
        /// Returns first element in the list.
        /// </summary>
        /// <typeparam name="TElement">Type of elements contained in the input list.</typeparam>
        /// <param name="array">An array of objects.</param>
        /// <returns>First element or null if the list is empty or null.</returns>
        public static TElement GetFirstObject<TElement>(Array array)
            where TElement : class
        {
            TElement firstElement;
            if (ArrayUtil.IsNullOrEmpty(array))
                firstElement = null;
            else
                firstElement = (TElement)array.GetValue(0);

            return firstElement;
        }

        /// <summary>
        /// Returns first element in the list.
        /// </summary>
        /// <typeparam name="TElement">Type of elements contained in the input list.</typeparam>
        /// <param name="collection">A collection of objects.</param>
        /// <returns>First element or null if the list is empty or null.</returns>
        public static TElement GetFirstObject<TElement>(IEnumerable collection)
            where TElement : class
        {
            TElement firstElement = null;
            if (collection != null)
            {
                foreach (TElement element in collection)
                {
                    firstElement = element;
                    break;
                }
            }

            return firstElement;
        }

        /// <summary>
        /// Sets first element of the list.
        /// </summary>
        /// <param name="list">A list of objects. If empty, the element will be added.</param>
        /// <param name="element">Object to set.</param>
        public static void SetFirstElement(IList list, object element)
        {
            if (list.Count > 0)
                list[0] = element;
            else
                list.Add(element);
        }

        /// <summary>
        /// Sets first element of the list.
        /// </summary>
        /// <typeparam name="TElement">Type of elements contained in the input list.</typeparam>
        /// <param name="list">A list of objects. If empty, the element will be added.</param>
        /// <param name="element">Object to set.</param>
        public static void SetFirstElement<TElement>(IList<TElement> list, TElement element)
        {
            if (list.Count > 0)
                list[0] = element;
            else
                list.Add(element);
        }

        #endregion

        #region Sets.

        /// <summary>
        /// Creates a union of two arrays.
        /// </summary>
        /// <param name="lhs">Left-hand side array.</param>
        /// <param name="rhs">Right-hand side array.</param>
        /// <returns>Union of arrays.</returns>
        public static int[] Union(int[] lhs, int[] rhs)
        {
            int[] union = new int[lhs.Length + rhs.Length];

            int idx = 0;
            foreach (int number in lhs)
                union[idx++] = number;
            foreach (int number in rhs)
                union[idx++] = number;

            return union;
        }

        /// <summary>
        /// Creates a union of two lists.
        /// </summary>
        /// <param name="lhs">Left-hand side list.</param>
        /// <param name="rhs">Right-hand side list.</param>
        /// <returns>Union of lists.</returns>
        public static ArrayList Union(IList lhs, IList rhs)
        {
            ArrayList unionAll = new ArrayList();
            unionAll.AddRange(lhs);
            unionAll.AddRange(rhs);
            return unionAll;
        }

        /// <summary>
        /// Creates a union of provided collections.
        /// </summary>
        /// <typeparam name="TList">Type of the list which is returned from the method.</typeparam>
        /// <typeparam name="TElement">Type of elements contained in the input and output collections.</typeparam>
        /// <param name="collections">Source collections whose elements will be coppied to union list.</param>
        /// <returns>A list of all objects in the provided collections.</returns>
        public static TList Union<TList, TElement>(params IEnumerable[] collections)
            where TList : IList, new()
        {
            TList united = new TList();
            foreach (IEnumerable list in collections)
            {
                foreach (TElement element in list)
                    united.Add(element);
            }

            return united;
        }

        /// <summary>
        /// Creates a union of provided arrays.
        /// </summary>
        /// <typeparam name="T">Type of elements contained in the input and output collections.</typeparam>
        /// <param name="arrays">Source arrays whose elements will be coppied to union array.</param>
        /// <returns>An array of all objects in the provided arrays.</returns>
        public static T[] Union<T>(params Array[] arrays)
        {
            int totalCount = 0;
            foreach (Array arr in arrays)
                totalCount += arr.Length;

            T[] united = new T[totalCount];
            int idxInUnion = 0;
            foreach (Array arr in arrays)
            {
                foreach (T element in arr)
                {
                    united[idxInUnion] = element;
                    idxInUnion++;
                }
            }

            return united;
        }

        /// <summary>
        /// Creates a distinct union of two arrays. All duplicates are eliminated.
        /// </summary>
        /// <param name="lhs">Left-hand side array.</param>
        /// <param name="rhs">Right-hand side array.</param>
        /// <returns>Distinct union.</returns>
        public static int[] UnionDistinct(int[] lhs, int[] rhs)
        {
            int[] unionAll = Union(lhs, rhs);
            int[] distinctUnion = EliminateDuplicates(unionAll);
            return distinctUnion;
        }

        /// <summary>
        /// Creates a distinct union of two lists. All object with duplicate key values are eliminated.
        /// </summary>
        /// <param name="lhs">Left-hand side list.</param>
        /// <param name="rhs">Right-hand side list.</param>
        /// <param name="uniqueField">Field with distinct values. Objects in both lists must contain this property/field.</param>
        /// <returns>Distinct union.</returns>
        public static ArrayList UnionDistinct(IList lhs, IList rhs, string uniqueField)
        {
            ArrayList distinctUnion = new ArrayList();
            foreach (object leftMember in lhs)
            {
                object keyValue = DataBinder.GetPropertyValue(leftMember, uniqueField);
                bool alreadyInDistinctList = (IndexOf(distinctUnion, uniqueField, keyValue) >= 0);
                if (!alreadyInDistinctList)
                    distinctUnion.Add(leftMember);
            }

            foreach (object rightMember in rhs)
            {
                object keyValue = DataBinder.GetPropertyValue(rightMember, uniqueField);
                bool alreadyInDistinctList = (IndexOf(distinctUnion, uniqueField, keyValue) >= 0);
                if (!alreadyInDistinctList)
                    distinctUnion.Add(rightMember);
            }

            return distinctUnion;
        }

        /// <summary>
        /// Creates a union of provided collections that contains only distinct values.
        /// </summary>
        /// <typeparam name="TList">Type of the list which is returned from the method.</typeparam>
        /// <typeparam name="TElement">Type of elements contained in the input and output collections.</typeparam>
        /// <param name="collections">Source collections whose elements will be coppied to union list.</param>
        /// <returns>A list of distinct values in the provided collections.</returns>
        public static TList UnionDistinct<TList, TElement>(params IEnumerable[] collections)
            where TList : IList, new()
        {
            TList unionAll = Union<TList, TElement>(collections);
            TList distinctUnion = EliminateDuplicates<TList, TElement>(unionAll);
            return distinctUnion;
        }

        /// <summary>
        /// Creates a union of provided collections that contains only objects with distinct keys.
        /// </summary>
        /// <typeparam name="TList">Type of the list which is returned from the method.</typeparam>
        /// <typeparam name="TElement">Type of elements contained in the input and output collections.</typeparam>
        /// <typeparam name="TKey">Type of the values in the key property.</typeparam>
        /// <param name="keyPropertyName">Name of the property that contains key values.</param>
        /// <param name="collections">Source collections whose elements will be coppied to union list.</param>
        /// <returns>A list of distinct objects in the provided collections.</returns>
        public static TList UnionDistinct<TList, TElement, TKey>(string keyPropertyName, params IEnumerable[] collections)
            where TList : IList, new()
        {
            TList unionAll = Union<TList, TElement>(collections);
            TList distinctUnion = EliminateDuplicates<TList, TElement, TKey>(unionAll, keyPropertyName);
            return distinctUnion;
        }

        /// <summary>
        /// Creates a union of provided collections that contains only objects with distinct keys.
        /// </summary>
        /// <typeparam name="TList">Type of the list which is returned from the method.</typeparam>
        /// <typeparam name="TElement">Type of elements contained in the input and output collections.</typeparam>
        /// <typeparam name="TKey">Type of the values in the key property.</typeparam>
        /// <param name="keyGetter">A delegate which gets the value of property that contains key values.</param>
        /// <param name="collections">Source collections whose elements will be coppied to union list.</param>
        /// <returns>A list of distinct objects in the provided collections.</returns>
        public static TList UnionDistinct<TList, TElement, TKey>(Delegates.Function<TKey, TElement> keyGetter, params IEnumerable[] collections)
            where TList : IList, new()
        {
            TList distinctUnion = new TList();
            Dictionary<TKey, object> usedKeys = new Dictionary<TKey, object>();
            foreach (IEnumerable col in collections)
            {
                foreach (TElement el in col)
                {
                    TKey key = keyGetter(el);
                    bool isAlreadyInList = usedKeys.ContainsKey(key);
                    if (!isAlreadyInList)
                    {
                        usedKeys.Add(key, null);
                        distinctUnion.Add(el);
                    }
                }
            }

            return distinctUnion;
        }

        /// <summary>
        /// Eliminates duplicates in the source array.
        /// </summary>
        /// <param name="arrayWithDuplicateValues">Array of integers that may contains duplicates.</param>
        /// <returns>Array of integers that contains only distinct values.</returns>
        public static int[] EliminateDuplicates(int[] arrayWithDuplicateValues)
        {
            ArrayList distinctValues = new ArrayList();
            foreach (int member in arrayWithDuplicateValues)
            {
                if (!Contains(distinctValues, member))
                    distinctValues.Add(member);
            }

            return ToIntArray(distinctValues);
        }

        /// <summary>
        /// Eliminates duplicate values in the source list.
        /// </summary>
        /// <typeparam name="TList">Type of the list which is returned from the method.</typeparam>
        /// <typeparam name="TElement">Type of elements contained in the input and output lists.</typeparam>
        /// <param name="listWithDuplicateValues">A list of values that may contain duplicates.</param>
        /// <returns>A list of distinct values.</returns>
        public static TList EliminateDuplicates<TList, TElement>(IEnumerable listWithDuplicateValues)
            where TList : IList, new()
        {
            TList distinctList = new TList();
            foreach (TElement element in listWithDuplicateValues)
            {
                bool alreadyInList = distinctList.Contains(element);
                if (!alreadyInList)
                    distinctList.Add(element);
            }

            return distinctList;
        }

        /// <summary>
        /// Eliminates objects with duplicate key values in the source list.
        /// </summary>
        /// <typeparam name="TList">Type of the list which is returned from the method.</typeparam>
        /// <typeparam name="TElement">Type of elements contained in the input and output lists.</typeparam>
        /// <typeparam name="TKey">Type of the values in the key property.</typeparam>
        /// <param name="listWithDuplicateValues">A list of objects that may contain duplicate key values.</param>
        /// <param name="keyPropertyName">Name of the property that contains key values.</param>
        /// <returns>A list of objects with distinct keys.</returns>
        public static TList EliminateDuplicates<TList, TElement, TKey>(IEnumerable listWithDuplicateValues, string keyPropertyName)
            where TList : IList, new()
        {
            TList distinctList = new TList();
            Dictionary<TKey, object> usedKeys = new Dictionary<TKey, object>();
            PropertyInfo keyProperty = typeof(TElement).GetProperty(keyPropertyName);
            foreach (TElement element in listWithDuplicateValues)
            {
                TKey currObjKey = (TKey)keyProperty.GetValue(element, null);
                bool alreadyInList = usedKeys.ContainsKey(currObjKey);
                if (!alreadyInList)
                {
                    distinctList.Add(element);
                    usedKeys.Add(currObjKey, null);
                }
            }

            return distinctList;
        }

        /// <summary>
        /// Eliminates duplicate objects in the source list.
        /// </summary>
        /// <typeparam name="TList">Type of the list which is returned from the method.</typeparam>
        /// <typeparam name="TElement">Type of elements contained in the input and output lists.</typeparam>
        /// <param name="listWithDuplicateValues">A list of objects that may contain duplicate key values.</param>
        /// <param name="comparison">The Comparison(TElement) delegate that defines how elements are compared.</param>
        /// <returns>A list of distinct objects.</returns>
        public static TList EliminateDuplicates<TList, TElement>(IEnumerable listWithDuplicateValues, Comparison<TElement> comparison)
            where TList : IList, new()
        {
            TList distinctList = new TList();
            foreach (TElement current in listWithDuplicateValues)
            {
                bool alreadyInCollection = Exists<TElement>(distinctList, (existing) => (0 == comparison(existing, current)));
                if (!alreadyInCollection)
                    distinctList.Add(current);
            }

            return distinctList;
        }

        /// <summary>
        /// Gets non-distinct objects from the source list.
        /// </summary>
        /// <typeparam name="TList">Type of the list which is returned from the method.</typeparam>
        /// <typeparam name="TElement">Type of elements contained in the input and output lists.</typeparam>
        /// <param name="listWithDuplicateValues">A list of objects that may contain duplicate key values.</param>
        /// <param name="comparison">The Comparison(TElement) delegate that defines how elements are compared.</param>
        /// <returns>A list of non-distinct objects.</returns>
        public static TList GetDuplicates<TList, TElement>(IEnumerable listWithDuplicateValues, Comparison<TElement> comparison)
            where TList : IList, new()
        {
            TList duplicates = new TList();
            foreach (TElement current in listWithDuplicateValues)
            {
                bool alreadyInCollection = Exists<TElement>(duplicates, (existing) => (0 == comparison(existing, current)));
                if (!alreadyInCollection)
                {
                    TList equalElements = FindAll<TList, TElement>(listWithDuplicateValues, (other) => (0 == comparison(other, current)));
                    if (equalElements.Count > 1)
                        duplicates.Add(current);
                }
            }

            return duplicates;
        }

        /// <summary>
        /// Checks if the provided collection contains duplicate elements.
        /// </summary>
        /// <typeparam name="TElement">Type of elements contained in the input collection.</typeparam>
        /// <param name="collection">A collection of objects that may contain duplicate key values.</param>
        /// <param name="comparison">The Comparison(TElement) delegate that defines how elements are compared.</param>
        /// <returns><b>true</b> if at least one duplicate element is found, ie. if provided comparison returns zero for any two elements in the collection; otherwise <b>false</b></returns>
        public static bool HasDuplicates<TElement>(IEnumerable collection, Comparison<TElement> comparison)
        {
            List<TElement> distinctList = new List<TElement>();
            foreach (TElement current in collection)
            {
                bool alreadyInCollection = Exists<TElement>(distinctList, (existing) => (0 == comparison(existing, current)));
                if (alreadyInCollection)
                    return true;
                else
                    distinctList.Add(current);
            }

            return false;
        }

        /// <summary>
        /// Removes specified values from source array.
        /// </summary>
        /// <param name="array">Source array.</param>
        /// <param name="valuesToBeExcludedFromArray">Values that are to be excluded from the source array.</param>
        /// <returns>Source array without the specified values.</returns>
        public static int[] GetDifference(int[] array, params int[] valuesToBeExcludedFromArray)
        {
            ArrayList difference = new ArrayList();
            foreach (int arrayMember in array)
            {
                if (!Contains(valuesToBeExcludedFromArray, arrayMember))
                    difference.Add(arrayMember);
            }

            return ToIntArray(difference);
        }

        /// <summary>
        /// Gets the set-theoretic difference of two provided values sets.
        /// </summary>
        /// <typeparam name="TList">Type of the list which is returned from the method.</typeparam>
        /// <typeparam name="TElement">Type of elements contained in the input and output lists.</typeparam>
        /// <param name="source">A set from which the elements/value are removed.</param>
        /// <param name="valuesToRemove">A set which contains the values to remove from the first set.</param>
        /// <returns>Source without the specified values.</returns>
        public static TList GetDifference<TList, TElement>(IEnumerable source, IEnumerable valuesToRemove)
            where TList : IList, new()
        {
            List<TElement> subtrahend = new List<TElement>();
            foreach (TElement elementToRemove in valuesToRemove)
                subtrahend.Add(elementToRemove);

            return GetDifference<TList, TElement>(source, subtrahend.ToArray());
        }

        /// <summary>
        /// Gets the set-theoretic difference of two provided values sets.
        /// </summary>
        /// <typeparam name="TList">Type of the list which is returned from the method.</typeparam>
        /// <typeparam name="TElement">Type of elements contained in the input and output lists.</typeparam>
        /// <param name="source">A set from which the elements/value are removed.</param>
        /// <param name="valuesToRemove">A set which contains the values to remove from the first set.</param>
        /// <returns>Source without the specified values.</returns>
        public static TList GetDifference<TList, TElement>(IEnumerable source, params TElement[] valuesToRemove)
            where TList : IList, new()
        {
            TList difference = new TList();
            foreach (TElement element in source)
            {
                bool survives = (Array.IndexOf<TElement>(valuesToRemove, element) < 0);
                if (survives)
                    difference.Add(element);
            }

            return difference;
        }

        /// <summary>
        /// Returns an intersection of two arrays.
        /// </summary>
        /// <param name="leftArray">First array.</param>
        /// <param name="rightArray">Second array.</param>
        /// <returns>Intersection. Contains only values that are found in both arrays.</returns>
        public static int[] Intersect(int[] leftArray, int[] rightArray)
        {
            ArrayList intersection = new ArrayList();
            foreach (int leftMember in leftArray)
            {
                if (Contains(rightArray, leftMember))
                    intersection.Add(leftMember);
            }

            return ToIntArray(intersection);
        }

        /// <summary>
        /// Returns an intersection of two lists.
        /// </summary>
        /// <param name="leftList">First list.</param>
        /// <param name="rightList">Second list.</param>
        /// <param name="keyProperty">Field used to compare objects. Objects in both lists must contain this property/field.</param>
        /// <returns>Intersection. Contains only objects that are found in both arrays.</returns>
        public static ArrayList Intersect(IList leftList, IList rightList, string keyProperty)
        {
            ArrayList intersection = new ArrayList();
            foreach (object leftMember in leftList)
            {
                object keyValue = DataBinder.GetPropertyValue(leftMember, keyProperty);
                bool isInBothLists = (IndexOf(rightList, keyProperty, keyValue) >= 0);
                if (isInBothLists)
                    intersection.Add(leftMember);
            }

            return intersection;
        }

        /// <summary>
        /// Gets the set-theoretic difference of two provided values sets.
        /// </summary>
        /// <typeparam name="TList">Type of the list which is returned from the method.</typeparam>
        /// <typeparam name="TElement">Type of elements contained in the input and output lists.</typeparam>
        /// <param name="leftList">First list.</param>
        /// <param name="rightList">Second list.</param>
        /// <returns>The set that contains all elements of 1st list that also belong to 2nd list.</returns>
        public static TList Intersect<TList, TElement>(IEnumerable leftList, IEnumerable rightList)
            where TList : IList, new()
            where TElement : IEquatable<TElement>
        {
            TList intersection = new TList();
            foreach (TElement leftMember in leftList)
            {
                bool isInBothLists = Contains<TElement>(rightList, leftMember);
                if (isInBothLists)
                    intersection.Add(leftMember);
            }

            return intersection;
        }

        /// <summary>
        /// Gets the set-theoretic difference of two provided values sets.
        /// </summary>
        /// <typeparam name="TList">Type of the list which is returned from the method.</typeparam>
        /// <typeparam name="TElement">Type of elements contained in the input and output lists.</typeparam>
        /// <typeparam name="TKey">Type of the values in the key property.</typeparam>
        /// <param name="leftList">First list.</param>
        /// <param name="rightList">Second list.</param>
        /// <param name="keyPropertyName">Name of the property that contains key values.</param>
        /// <returns>The set that contains all elements of 1st list that also belong to 2nd list.</returns>
        public static TList Intersect<TList, TElement, TKey>(IEnumerable leftList, IEnumerable rightList, string keyPropertyName)
            where TList : IList, new()
            where TKey : IEquatable<TKey>
        {
            TList intersection = new TList();
            PropertyInfo keyProperty = typeof(TElement).GetProperty(keyPropertyName);
            foreach (TElement leftMember in leftList)
            {
                TKey key = (TKey)keyProperty.GetValue(leftMember, null);
                bool isInBothLists = Contains<TKey>(rightList, keyPropertyName, key);
                if (isInBothLists)
                    intersection.Add(leftMember);
            }

            return intersection;
        }

        /// <summary>
        /// Checks whether an array contains a given value.
        /// </summary>
        /// <param name="array">Array.</param>
        /// <param name="val">Value to find in array.</param>
        /// <returns>True if array contains the given value; false otherwise.</returns>
        public static bool Contains(object[] array, object val)
        {
            foreach (object arrayMember in array)
            {
                if (arrayMember == val)
                    return true;
            }

            return false;
        }

        /// <summary>
        /// Checks whether an array contains a given value.
        /// </summary>
        /// <param name="array">Array.</param>
        /// <param name="val">Value to find in array.</param>
        /// <returns>True if array contains the given value; false otherwise.</returns>
        public static bool Contains(int[] array, int val)
        {
            foreach (int arrayMember in array)
            {
                if (arrayMember == val)
                    return true;
            }

            return false;
        }

        /// <summary>
        /// Checks whether a list contains a given integer value.
        /// </summary>
        /// <param name="integers">List of integers.</param>
        /// <param name="val">Value to find in array.</param>
        /// <returns>True if array contains the given value; false otherwise.</returns>
        private static bool Contains(IList integers, int val)
        {
            foreach (int arrayMember in integers)
            {
                if (arrayMember == val)
                    return true;
            }

            return false;
        }

        /// <summary>
        /// Checks whether a collection contains the specified value.
        /// </summary>
        /// <typeparam name="TElement">Type of elements in the collection.</typeparam>
        /// <param name="collection">A collection which will be scanned.</param>
        /// <param name="value">Value to find the collection.</param>
        /// <returns><b>true</b> if array contains the given value; <b>false</b> otherwise.</returns>
        public static bool Contains<TElement>(IEnumerable collection, TElement value)
            where TElement : IEquatable<TElement>
        {
            foreach (TElement element in collection)
            {
                bool isMatch = element.Equals(value);
                if (isMatch)
                    return true;
            }

            return false;
        }

        /// <summary>
        /// Checks whether a collection contains an object with the specified key value.
        /// </summary>
        /// <typeparam name="TKey">Type of the values in the key property.</typeparam>
        /// <param name="collection">A list or an array of objects.</param>
        /// <param name="keyPropertyName">Name of the property that contains key values.</param>
        /// <param name="key">Key value of object that is to be found in the source list.</param>
        /// <returns><b>true</b> if array contains the given value; <b>false</b> otherwise.</returns>
        public static bool Contains<TKey>(IEnumerable collection, string keyPropertyName, TKey key)
            where TKey : IEquatable<TKey>
        {
            int index = IndexOf<TKey>(collection, keyPropertyName, key);
            bool isFound = (index >= 0) ? true : false;
            return isFound;
        }

        /// <summary>Checks if the thw provided collection contains equal value.</summary>
        /// <param name="leftList">A collection.</param>
        /// <param name="rightList">A collection.</param>
        /// <returns><b>true</b> if both colleciton contains equal values; otherwise <b>false</b></returns>
        public static bool ContainEqualValues(IEnumerable leftList, IEnumerable rightList)
        {
            if (leftList == null)
                return (rightList == null);

            if (rightList == null)
                return (leftList == null);

            IEnumerator leftEnumerator = leftList.GetEnumerator();
            IEnumerator rightEnumerator = rightList.GetEnumerator();
            while (leftEnumerator.MoveNext())
            {
                bool existsNextMemberInRightList = rightEnumerator.MoveNext();
                if (!existsNextMemberInRightList)
                    return false;

                if (leftEnumerator.Current == null && rightEnumerator.Current != null)
                    return false;

                if (rightEnumerator.Current == null && leftEnumerator.Current != null)
                    return false;

                string leftValue = System.Convert.ToString(leftEnumerator.Current, CultureInfo.InvariantCulture);
                string rightValue = System.Convert.ToString(rightEnumerator.Current, CultureInfo.InvariantCulture);
                if (leftValue != rightValue)
                    return false;
            }

            bool isRightListLongerThanLeftList = rightEnumerator.MoveNext();
            if (isRightListLongerThanLeftList)
                return false;
            else
                return true;
        }

        #endregion

        #region Aggregate functions.

        /// <summary>
        /// Returns the smallest value in the array.
        /// </summary>
        /// <param name="numbers">An array of numbers to inspect.</param>
        /// <returns>The smallest number. Zero if collection iz empty.</returns>
        public static int Min(params int[] numbers)
        {
            return Min<int>(numbers);
        }

        /// <summary>
        /// Returns the smallest value in the array.
        /// </summary>
        /// <typeparam name="TElement">Type of elements in the collection.</typeparam>
        /// <param name="array">An array of values to inspect.</param>
        /// <returns>The smallest element. Default type value if collection iz empty.</returns>
        public static TElement Min<TElement>(params TElement[] array)
            where TElement : IComparable<TElement>
        {
            return Min<TElement>((IEnumerable)array);
        }

        /// <summary>
        /// Returns the smallest value in the collection.
        /// </summary>
        /// <typeparam name="TElement">Type of elements in the collection.</typeparam>
        /// <param name="collection">A collection of values to inspect.</param>
        /// <returns>The smallest element. Default type value if collection iz empty.</returns>
        public static TElement Min<TElement>(IEnumerable collection)
            where TElement : IComparable<TElement>
        {
            IEnumerator vector = collection.GetEnumerator();
            bool isEmpty = !vector.MoveNext();
            if (isEmpty)
                return default(TElement);

            TElement min = (TElement)vector.Current;
            foreach (TElement element in collection)
            {
                bool isLessThanMin = (element.CompareTo(min) < 0);
                if (isLessThanMin)
                    min = element;
            }

            return min;
        }

        /// <summary>
        /// Returns the object with the smallest comparation property value.
        /// </summary>
        /// <typeparam name="TElement">Type of elements in the collection. Must be a reference type, ie. class.</typeparam>
        /// <typeparam name="TComparableProperty">Type of the values in the property by which the objects are compared.</typeparam>
        /// <param name="collection">A collection of objects to inspect.</param>
        /// <param name="comparablePropertyName">Name of the property by which the object are compared.</param>
        /// <returns>Object with the smallest comparation value. <b>null</b> if collection is empty.</returns>
        public static TElement Min<TElement, TComparableProperty>(IEnumerable collection, string comparablePropertyName)
            where TElement : class
            where TComparableProperty : IComparable<TComparableProperty>
        {
            IEnumerator vector = collection.GetEnumerator();
            bool isEmpty = !vector.MoveNext();
            if (isEmpty)
                return default(TElement);

            PropertyInfo comparableProperty = typeof(TElement).GetProperty(comparablePropertyName);
            TElement objWithMinProperty = (TElement)vector.Current;
            TComparableProperty minProperty = (TComparableProperty)comparableProperty.GetValue(objWithMinProperty, null);
            foreach (TElement element in collection)
            {
                TComparableProperty compareBy = (TComparableProperty)comparableProperty.GetValue(element, null);
                bool isLessThanMin = (compareBy.CompareTo(minProperty) < 0);
                if (isLessThanMin)
                {
                    minProperty = compareBy;
                    objWithMinProperty = element;
                }
            }

            return objWithMinProperty;
        }

        /// <summary>
        /// Returns the object with the smallest comparisson value.
        /// </summary>
        /// <typeparam name="TElement">Type of elements in the collection. Must be a reference type, ie. class.</typeparam>
        /// <param name="collection">A collection of objects to inspect.</param>
        /// <param name="comparison">The Comparison(TElement) delegate that defines how elements are compared.</param>
        /// <returns>Object with the smallest comparation value. <b>null</b> if collection is empty.</returns>
        public static TElement Min<TElement>(IEnumerable collection, Comparison<TElement> comparison)
            where TElement : class
        {
            IEnumerator vector = collection.GetEnumerator();
            bool isEmpty = !vector.MoveNext();
            if (isEmpty)
                return default(TElement);

            TElement minElement = (TElement)vector.Current;
            foreach (TElement currElement in collection)
            {
                bool isLessThanMin = comparison(currElement, minElement) < 0;
                if (isLessThanMin)
                    minElement = currElement;
            }

            return minElement;
        }

        /// <summary>
        /// Returns the object with the smallest comparisson value.
        /// </summary>
        /// <typeparam name="TElement">Type of elements in the collection. Must be a reference type, ie. class.</typeparam>
        /// <typeparam name="TComparableProperty">Type of the values in the property by which the objects are compared.</typeparam>
        /// <param name="collection">A collection of objects to inspect.</param>
        /// <param name="compareByGetter">A delegate which gets the value of property by which the objects are compared.</param>
        /// <returns>Object with the smallest comparation value. <b>null</b> if collection is empty.</returns>
        public static TElement Min<TElement, TComparableProperty>(IEnumerable collection, Delegates.Function<TComparableProperty, TElement> compareByGetter)
            where TElement : class
            where TComparableProperty : IComparable<TComparableProperty>
        {
            IEnumerator vector = collection.GetEnumerator();
            bool isEmpty = !vector.MoveNext();
            if (isEmpty)
                return default(TElement);

            TElement minElement = (TElement)vector.Current;
            foreach (TElement currElement in collection)
            {
                TComparableProperty maxValue = compareByGetter(minElement);
                TComparableProperty currValue = compareByGetter(currElement);
                bool isLessThanMax = currValue.CompareTo(maxValue) < 0;
                if (isLessThanMax)
                    minElement = currElement;
            }

            return minElement;
        }

        /// <summary>
        /// Returns the smallest value in the collection or null.
        /// </summary>
        /// <typeparam name="TValue">Type of elements in the collection.</typeparam>
        /// <param name="collection">A collection of values to inspect.</param>
        /// <returns>The smallest value. <b>null</b> if collection is empty.</returns>
        public static TValue? MinValue<TValue>(IEnumerable collection)
            where TValue : struct, IComparable<TValue>
        {
            IEnumerator vector = collection.GetEnumerator();
            bool isEmpty = !vector.MoveNext();
            if (isEmpty)
                return null;

            TValue min = (TValue)vector.Current;
            foreach (TValue element in collection)
            {
                bool isLessThanMin = (element.CompareTo(min) < 0);
                if (isLessThanMin)
                    min = element;
            }

            return min;
        }

        /// <summary>
        /// Returns the smallest value in the collection or null.
        /// </summary>
        /// <typeparam name="TElement">Type of elements in the collection. Must be a reference type, ie. class.</typeparam>
        /// <typeparam name="TValue">Type of the values in the property which is compared.</typeparam>
        /// <param name="collection">A collection of objects whose properties are inspected.</param>
        /// <param name="valueGetter">A delegate which gets the value of property which is compared.</param>
        /// <returns>The smallest value. <b>null</b> if collection is empty.</returns>
        public static TValue? MinValue<TElement, TValue>(IEnumerable collection, Delegates.Function<TValue, TElement> valueGetter)
            where TElement : class
            where TValue : struct, IComparable<TValue>
        {
            TElement minElement = Min<TElement, TValue>(collection, valueGetter);
            if (minElement != null)
                return valueGetter(minElement);
            else
                return null;
        }

        /// <summary>
        /// Returns the greatest value in the array.
        /// </summary>
        /// <param name="numbers">Array of numbers to inspect. Zero if collection is empty.</param>
        /// <returns>The greatest number.</returns>
        public static int Max(params int[] numbers)
        {
            return Max<int>(numbers);
        }

        /// <summary>
        /// Returns the greatest value in the array.
        /// </summary>
        /// <typeparam name="TElement">Type of elements in the collection.</typeparam>
        /// <param name="array">An array of values to inspect.</param>
        /// <returns>The greatest element. Default type value if collection is empty.</returns>
        public static TElement Max<TElement>(params TElement[] array)
            where TElement : IComparable<TElement>
        {
            return Max<TElement>((IEnumerable)array);
        }

        /// <summary>
        /// Returns the greatest value in the collection.
        /// </summary>
        /// <typeparam name="TElement">Type of elements in the collection.</typeparam>
        /// <param name="collection">A collection of values to inspect.</param>
        /// <returns>The greatest element. Default type value if collection is empty.</returns>
        public static TElement Max<TElement>(IEnumerable collection)
            where TElement : IComparable<TElement>
        {
            IEnumerator vector = collection.GetEnumerator();
            bool isEmpty = !vector.MoveNext();
            if (isEmpty)
                return default(TElement);

            TElement max = (TElement)vector.Current;
            foreach (TElement element in collection)
            {
                bool isGreaterThanMax = (element.CompareTo(max) > 0);
                if (isGreaterThanMax)
                    max = element;
            }

            return max;
        }

        /// <summary>
        /// Returns the object with the greatest comparation property value.
        /// </summary>
        /// <typeparam name="TElement">Type of elements in the collection. Must be a reference type, ie. class.</typeparam>
        /// <typeparam name="TComparableProperty">Type of the values in the property by which the object are compared.</typeparam>
        /// <param name="collection">A collection of objects to inspect.</param>
        /// <param name="comparablePropertyName">Name of the property by which the object are compared.</param>
        /// <returns>Object with the greatest comparation value. <b>null</b> if collection is empty.</returns>
        public static TElement Max<TElement, TComparableProperty>(IEnumerable collection, string comparablePropertyName)
            where TElement : class
            where TComparableProperty : IComparable<TComparableProperty>
        {
            IEnumerator vector = collection.GetEnumerator();
            bool isEmpty = !vector.MoveNext();
            if (isEmpty)
                return default(TElement);

            PropertyInfo comparableProperty = typeof(TElement).GetProperty(comparablePropertyName);
            TElement objWithMaxProperty = (TElement)vector.Current;
            TComparableProperty maxProperty = (TComparableProperty)comparableProperty.GetValue(objWithMaxProperty, null);
            foreach (TElement element in collection)
            {
                TComparableProperty compareBy = (TComparableProperty)comparableProperty.GetValue(element, null);
                bool isGreaterThanMax = (compareBy.CompareTo(maxProperty) > 0);
                if (isGreaterThanMax)
                {
                    maxProperty = compareBy;
                    objWithMaxProperty = element;
                }
            }

            return objWithMaxProperty;
        }

        /// <summary>
        /// Returns the object with the greatest comparisson value.
        /// </summary>
        /// <typeparam name="TElement">Type of elements in the collection. Must be a reference type, ie. class.</typeparam>
        /// <param name="collection">A collection of objects to inspect.</param>
        /// <param name="comparison">The Comparison(TElement) delegate that defines how elements are compared.</param>
        /// <returns>Object with the greatest comparation value. <b>null</b> if collection is empty.</returns>
        public static TElement Max<TElement>(IEnumerable collection, Comparison<TElement> comparison)
            where TElement : class
        {
            IEnumerator vector = collection.GetEnumerator();
            bool isEmpty = !vector.MoveNext();
            if (isEmpty)
                return default(TElement);

            TElement maxElement = (TElement)vector.Current;
            foreach (TElement currElement in collection)
            {
                bool isGreaterThanMax = comparison(currElement, maxElement) > 0;
                if (isGreaterThanMax)
                    maxElement = currElement;
            }

            return maxElement;
        }

        /// <summary>
        /// Returns the object with the greatest comparisson value.
        /// </summary>
        /// <typeparam name="TElement">Type of elements in the collection. Must be a reference type, ie. class.</typeparam>
        /// <typeparam name="TComparableProperty">Type of the values in the property by which the objects are compared.</typeparam>
        /// <param name="collection">A collection of objects to inspect.</param>
        /// <param name="compareByGetter">A delegate which gets the value tha value of property by which the objects are compared.</param>
        /// <returns>Object with the greatest comparation value. <b>null</b> if collection is empty.</returns>
        public static TElement Max<TElement, TComparableProperty>(IEnumerable collection, Delegates.Function<TComparableProperty, TElement> compareByGetter)
            where TElement : class
            where TComparableProperty : IComparable<TComparableProperty>
        {
            IEnumerator vector = collection.GetEnumerator();
            bool isEmpty = !vector.MoveNext();
            if (isEmpty)
                return default(TElement);

            TElement maxElement = (TElement)vector.Current;
            foreach (TElement currElement in collection)
            {
                TComparableProperty maxValue = compareByGetter(maxElement);
                TComparableProperty currValue = compareByGetter(currElement);
                bool isGreaterThanMax = currValue.CompareTo(maxValue) > 0;
                if (isGreaterThanMax)
                    maxElement = currElement;
            }

            return maxElement;
        }

        /// <summary>
        /// Returns the greatest value in the collection or null.
        /// </summary>
        /// <typeparam name="TValue">Type of elements in the collection.</typeparam>
        /// <param name="collection">A collection of values to inspect.</param>
        /// <returns>The greatest value. <b>null</b> if collection is empty.</returns>
        public static TValue? MaxValue<TValue>(IEnumerable collection)
            where TValue : struct, IComparable<TValue>
        {
            IEnumerator vector = collection.GetEnumerator();
            bool isEmpty = !vector.MoveNext();
            if (isEmpty)
                return null;

            TValue max = (TValue)vector.Current;
            foreach (TValue element in collection)
            {
                bool isGreaterThanMax = (element.CompareTo(max) > 0);
                if (isGreaterThanMax)
                    max = element;
            }

            return max;
        }

        /// <summary>
        /// Returns the greatest value in the collection or null.
        /// </summary>
        /// <typeparam name="TElement">Type of elements in the collection. Must be a reference type, ie. class.</typeparam>
        /// <typeparam name="TValue">Type of the values in the property which is compared.</typeparam>
        /// <param name="collection">A collection of objects whose properties are inspected.</param>
        /// <param name="valueGetter">A delegate which gets the value of property which is compared.</param>
        /// <returns>The greatest value. <b>null</b> if collection is empty.</returns>
        public static TValue? MaxValue<TElement, TValue>(IEnumerable collection, Delegates.Function<TValue, TElement> valueGetter)
            where TElement : class
            where TValue : struct, IComparable<TValue>
        {
            TElement maxElement = Max<TElement, TValue>(collection, valueGetter);
            if (maxElement != null)
                return valueGetter(maxElement);
            else
                return null;
        }

        /// <summary>
        /// Returns the number of elements that match the conditions defined by the specified predicate.  
        /// </summary>
        /// <typeparam name="TElement">Type of elements contained in the collection.</typeparam>
        /// <param name="collection">A collection.</param>
        /// <param name="match">The Predicate(TElement) delegate that defines the conditions of the elements to search for.</param>
        /// <returns>Number of elements that match the conditions defined by the specified predicate.</returns>
        public static int Count<TElement>(IEnumerable collection, Predicate<TElement> match)
        {
            int count = 0;
            foreach (TElement obj in collection)
            {
                if (match(obj))
                    count++;
            }

            return count;
        }

        #endregion

        #region Sorting.

        /// <summary>
        /// Sorts a list using default comparer.
        /// </summary>
        /// <param name="objectsToSort">A list of objects to sort.</param>
        /// <returns>Sorted objects.</returns>
        public static ArrayList Sort(IEnumerable objectsToSort)
        {
            ArrayList list = ToArrayList(objectsToSort);
            list.Sort();
            return list;
        }

        class PropertyComparer : IComparer
        {
            static readonly Comparer valueComparer = new Comparer(CultureInfo.InvariantCulture);
            string property;

            public PropertyComparer(string property)
            {
                this.property = property;
            }

            public int Compare(object x, object y)
            {
                object xProperty = DataBinder.Eval(x, this.property);
                object yProperty = DataBinder.Eval(y, this.property);

                return valueComparer.Compare(xProperty, yProperty);
            }
        }

        /// <summary>
        /// Sorts a list.
        /// </summary>
        /// <param name="objectsToSort">A list of objects to sort.</param>
        /// <param name="orderByProperty">Object property whose values are to be sorted.</param>
        /// <returns>Sorted objects.</returns>
        public static ArrayList Sort(IEnumerable objectsToSort, string orderByProperty)
        {
            ArrayList sortedList = ToArrayList(objectsToSort);
            sortedList.Sort(new PropertyComparer(orderByProperty));
            return sortedList;
        }

        /// <summary>
        /// Sorts a list.
        /// </summary>
        /// <param name="objectsToSort">A list of objects to sort.</param>
        /// <param name="orderByProperty">Object property whose values are to be sorted.</param>
        /// <param name="ascending">Specifies whether the values in the specified property should be sorted in ascending order, from lowest value to highest value.</param>
        /// <returns>Sorted objects.</returns>
        public static ArrayList Sort(IEnumerable objectsToSort, string orderByProperty, bool ascending)
        {
            ArrayList sortedObjects = Sort(objectsToSort, orderByProperty);
            if (!ascending)
                sortedObjects.Reverse();

            return sortedObjects;
        }

        /// <summary>
        /// Sorts a collection using default comparer.
        /// </summary>
        /// <typeparam name="TList">Type of the list which is returned from the method.</typeparam>
        /// <typeparam name="TElement">Type of elements contained in the collection.</typeparam>
        /// <param name="collection">A collection of objects to sort.</param>
        /// <returns>Sorted list.</returns>
        public static TList Sort<TList, TElement>(IEnumerable collection)
            where TList : IList, new()
            where TElement : IComparable
        {
            TList sortedList = new TList();
            MethodInfo sortMethod = typeof(TList).GetMethod("Sort", new Type[0]);
            if (sortMethod != null)
            {
                Copy(collection, sortedList);
                sortMethod.Invoke(sortedList, null);
            }
            else
            {
                List<TElement> intermediateSortedList = new List<TElement>();
                Copy(collection, intermediateSortedList);
                intermediateSortedList.Sort();
                Copy(intermediateSortedList, sortedList);
            }

            return sortedList;
        }

        private static void Copy(IEnumerable source, IList target)
        {
            foreach (object obj in source)
                target.Add(obj);
        }

        /// <summary>
        /// Sorts a collection using the specified comparison.
        /// </summary>
        /// <typeparam name="TList">Type of the list which is returned from the method.</typeparam>
        /// <typeparam name="TElement">Type of elements contained in the collection.</typeparam>
        /// <param name="collection">A collection of objects to sort.</param>
        /// <param name="comparison">The Comparison(TElement) delegate that defines how elements are compared.</param>
        /// <returns>Sorted list.</returns>
        public static TList Sort<TList, TElement>(IEnumerable collection, Comparison<TElement> comparison)
            where TList : IList, new()
        {
            TList sortedList = new TList();
            MethodInfo sortMethod = typeof(TList).GetMethod("Sort", new Type[] { typeof(Comparison<TElement>) });
            if (sortMethod != null)
            {
                Copy(collection, sortedList);
                sortMethod.Invoke(sortedList, new object[] { comparison });
            }
            else
            {
                List<TElement> intermediateSortedList = new List<TElement>();
                Copy(collection, intermediateSortedList);
                intermediateSortedList.Sort(comparison);
                Copy(intermediateSortedList, sortedList);
            }

            return sortedList;
        }

        /// <summary>
        /// Sorts a collection.
        /// </summary>
        /// <typeparam name="TList">Type of the list which is returned from the method.</typeparam>
        /// <typeparam name="TElement">Type of elements contained in the collection.</typeparam>
        /// <typeparam name="TComparableProperty">Type of the values in the property by which the objects are ordered.</typeparam>
        /// <param name="collection">A collection of objects to sort.</param>
        /// <param name="orderByGetter">A delegate which gets the value tha value of property by which the objects are ordered.</param>
        /// <returns>Sorted list.</returns>
        public static TList Sort<TList, TElement, TComparableProperty>(IEnumerable collection, Delegates.Function<TComparableProperty, TElement> orderByGetter)
            where TList : IList, new()
            where TComparableProperty : IComparable<TComparableProperty>
        {
            return Sort<TList, TElement, TComparableProperty>(collection, /*asc*/ true, orderByGetter);
        }

        /// <summary>
        /// Sorts a collection.
        /// </summary>
        /// <typeparam name="TList">Type of the list which is returned from the method.</typeparam>
        /// <typeparam name="TElement">Type of elements contained in the collection.</typeparam>
        /// <typeparam name="TComparableProperty">Type of the values in the property by which the objects are ordered.</typeparam>
        /// <param name="collection">A collection of objects to sort.</param>
        /// <param name="ascending">Specifies whether the values in the specified property should be sorted in ascending order, from lowest value to highest value.</param>
        /// <param name="orderByGetter">A delegate which gets the value tha value of property by which the objects are ordered.</param>
        /// <returns>Sorted list.</returns>
        public static TList Sort<TList, TElement, TComparableProperty>(IEnumerable collection, bool ascending, Delegates.Function<TComparableProperty, TElement> orderByGetter)
            where TList : IList, new()
            where TComparableProperty : IComparable<TComparableProperty>
        {
            Comparison<TElement> comparison;
            if (ascending)
                comparison = (x, y) => orderByGetter(x).CompareTo(orderByGetter(y));
            else
                comparison = (x, y) => orderByGetter(y).CompareTo(orderByGetter(x));

            return Sort<TList, TElement>(collection, comparison);
        }

        #endregion

        #region Concat.

        /// <summary>
        /// Concatenates the string representations of the elements in the specified list.  
        /// </summary>
        /// <param name="list">A list of objects.</param>
        /// <param name="delimiter">Optional string which is used to delimit objects. Nullable.</param>
        /// <returns>The concatenated and delimited string representations of the values of the elements in the list.</returns>
        public static string Concat(IEnumerable list, string delimiter)
        {
            if (delimiter == null)
                delimiter = "";

            StringBuilder str = new StringBuilder();
            foreach (object element in list)
            {
                str.Append(element);
                str.Append(delimiter);
            }

            if (str.Length >= delimiter.Length)
                str.Remove(str.Length - delimiter.Length, delimiter.Length);

            return str.ToString();
        }

        /// <summary>
        /// Concatenates the string representations of the elements in the specified list.  
        /// </summary>
        /// <param name="list">A list of objects.</param>
        /// <param name="delimiter">Optional string which is used to delimit objects. Nullable.</param>
        /// <param name="formatter">Formats elements of the list.</param>
        /// <returns>The concatenated and delimited string representations of the values of the elements in the list.</returns>
        public static string Concat(IEnumerable list, string delimiter, IMessageFormatter formatter)
        {
            if (delimiter == null)
                delimiter = "";

            StringBuilder str = new StringBuilder();
            foreach (object element in list)
            {
                str.Append(formatter.Format(element));
                str.Append(delimiter);
            }

            if (str.Length >= delimiter.Length)
                str.Remove(str.Length - delimiter.Length, delimiter.Length);

            return str.ToString();
        }

        /// <summary>
        /// Concatenates the string representations of the elements in the specified list.  
        /// </summary>
        /// <typeparam name="TElement">Type of elements contained in the collection.</typeparam>
        /// <param name="collection">A collection of objects that contain the values to concatenate.</param>
        /// <param name="delimiter">Optional string which is used to delimit objects. Nullable.</param>
        /// <param name="getValueToConcat">A delegate to a method which returns value to concatenate.</param>
        /// <returns>The concatenated and delimited string representations of the values of the elements in the list.</returns>
        public static string Concat<TElement>(IEnumerable collection, string delimiter, Delegates.Function<object, TElement> getValueToConcat)
        {
            if (delimiter == null)
                delimiter = "";

            StringBuilder str = new StringBuilder();
            foreach (TElement element in collection)
            {
                object valToConcat = getValueToConcat(element);
                str.Append(valToConcat);
                str.Append(delimiter);
            }

            if (str.Length >= delimiter.Length)
                str.Remove(str.Length - delimiter.Length, delimiter.Length);

            return str.ToString();
        }

        /// <summary>
        /// Concatenates the string representations of the elements in the specified list.  
        /// </summary>
        /// <typeparam name="TElement">Type of elements contained in the collection.</typeparam>
        /// <param name="collection">A collection of objects that contain the values to concatenate.</param>
        /// <param name="delimiter">Optional string which is used to delimit objects. Nullable.</param>
        /// <param name="formatter">Formats elements of the list.</param>
        /// <param name="getValueToConcat">A delegate to a method which returns value to concatenate.</param>
        /// <returns>The concatenated and delimited string representations of the values of the elements in the list.</returns>
        public static string Concat<TElement>(IEnumerable collection, string delimiter, IMessageFormatter formatter, Delegates.Function<object, TElement> getValueToConcat)
        {
            if (delimiter == null)
                delimiter = "";

            StringBuilder str = new StringBuilder();
            foreach (TElement element in collection)
            {
                object valToConcat = getValueToConcat(element);
                str.Append(formatter.Format(valToConcat));
                str.Append(delimiter);
            }

            if (str.Length >= delimiter.Length)
                str.Remove(str.Length - delimiter.Length, delimiter.Length);

            return str.ToString();
        }

        #endregion

        #region ForEach.

        /// <summary>
        /// Performs the specified action on each element of the collection.
        /// </summary>
        /// <typeparam name="TElement">Type of elements contained in the collection.</typeparam>
        /// <param name="collection">A collection.</param>
        /// <param name="action">The Action(TElement) delegate to perform on each element of the collection.</param>
        public static void ForEach<TElement>(IEnumerable collection, Action<TElement> action)
        {
            foreach (TElement el in collection)
                action(el);
        }

        #endregion

        #region NextNegativeId.

        /// <summary>Computes a temporary negative ID which uniquely identifies a new record in the given collection.</summary>
        /// <typeparam name="TElement">Type of objects in the collection. Typically these are entities, business objects or ADO.NET data rows.</typeparam>
        /// <param name="collection">A collection which is to be inspected.</param>
        /// <param name="getElementId">A method which returns object's ID.</param>
        /// <returns>A negative integer.</returns>
        public static short NextNegativeId<TElement>(IEnumerable collection, Delegates.Function<short, TElement> getElementId)
        {
            short smallestId = 0;
            foreach (TElement el in collection)
            {
                short elementId = getElementId(el);
                if (elementId < smallestId)
                    smallestId = elementId;
            }

            short nextId = (short)(smallestId - 1);
            return nextId;
        }

        /// <summary>Computes a temporary negative ID which uniquely identifies a new record in the given collection.</summary>
        /// <typeparam name="TElement">Type of objects in the collection. Typically these are entities, business objects or ADO.NET data rows.</typeparam>
        /// <param name="collection">A collection which is to be inspected.</param>
        /// <param name="getElementId">A method which returns object's ID.</param>
        /// <returns>A negative integer.</returns>
        public static int NextNegativeId<TElement>(IEnumerable collection, Delegates.Function<int, TElement> getElementId)
        {
            int smallestId = 0;
            foreach (TElement el in collection)
            {
                int elementId = getElementId(el);
                if (elementId < smallestId)
                    smallestId = elementId;
            }

            int nextId = smallestId - 1;
            return nextId;
        }

        /// <summary>Computes a temporary negative ID which uniquely identifies a new record in the given collection.</summary>
        /// <typeparam name="TElement">Type of objects in the collection. Typically these are entities, business objects or ADO.NET data rows.</typeparam>
        /// <param name="collection">A collection which is to be inspected.</param>
        /// <param name="getElementId">A method which returns object's ID.</param>
        /// <returns>A negative integer.</returns>
        public static long NextNegativeId<TElement>(IEnumerable collection, Delegates.Function<long, TElement> getElementId)
        {
            long smallestId = 0;
            foreach (TElement el in collection)
            {
                long elementId = getElementId(el);
                if (elementId < smallestId)
                    smallestId = elementId;
            }

            long nextId = smallestId - 1;
            return nextId;
        }

        /// <summary>Computes a temporary negative ID which uniquely identifies a new record in the given collection.</summary>
        /// <typeparam name="TElement">Type of objects in the collection. Typically these are entities, business objects or ADO.NET data rows.</typeparam>
        /// <param name="collection">A collection which is to be inspected.</param>
        /// <param name="getElementId">A method which returns object's ID.</param>
        /// <returns>A string that represents a negative integer.</returns>
        public static string NextNegativeId<TElement>(IEnumerable collection, Delegates.Function<string, TElement> getElementId)
        {
            int smallestId = 0;
            foreach (TElement el in collection)
            {
                string textualId = getElementId(el);
                int? elementId = NumberParser.ParseInt(textualId);
                if (elementId.HasValue && elementId.Value < smallestId)
                    smallestId = elementId.Value;
            }

            int nextId = smallestId - 1;
            return nextId.ToString(CultureInfo.InvariantCulture);
        }

        #endregion

        /// <summary>Returns array with all elements except one.</summary>
        /// <typeparam name="T">Type of elements in the array.</typeparam>
        /// <param name="input">Input array.</param>
        /// <param name="itemToRemove">Excluded item.</param>
        /// <returns>Array without the specified element.</returns>
        public static T[] Except<T>(T[] input, T itemToRemove)
        {
            List<T> output = new List<T>();
            foreach (T item in input)
            {
                if (!item.Equals(itemToRemove))
                    output.Add(item);
            }

            return output.ToArray();
        }
    }
}